#ifndef BOUNDARYPATCH_HPP
#define BOUNDARYPATCH_HPP

#include "meshTypes_fwd.hpp"
#include "signalCodes.hpp"
#include <string>

namespace hms
{

/**
 * @brief Represents a boundary patch, i.e. a segment of a mesh's boundary,
 * defined by the edges at that boundary. Provides access to the domain and
 * ghost cell indices at that boundary patch.
 * 
 */
class BoundaryPatch
{
private:
	std::string m_name;
	ArrayXi
		m_edges,
		m_domainCells,
		m_ghostCells;
	BoundaryLocation m_location; // only useful for struct mesh types
	int m_normalSign {signalCode::uninitialised};
	
public:
	BoundaryPatch() = default;

	BoundaryPatch( std::string );
	
	/**
	 * @brief Constructs a boundary patch from the provided name, mesh and edge
	 * indices. Automatically fetches and sets domain and ghost cell indices.
	 * 
	 * @tparam MeshType 
	 * @param edges 
	 */
	template<typename MeshType>
	BoundaryPatch(
		std::string,
		const MeshBase<MeshType>&,
		ArrayXi&& edges
	);
	
	/**
	 * @brief Constructs a boundary patch from name, edge, domain and ghost cell
	 * indices. Intended for initialisation from file.
	 * 
	 * @param name
	 * @param edges 
	 * @param domainCells 
	 * @param ghostCells 
	 */
	template<typename MeshType>
	BoundaryPatch(
		std::string name,
		const MeshBase<MeshType>&,
		ArrayXi&& edges,
		ArrayXi&& domainCells,
		ArrayXi&& ghostCells
	);

	auto name() const -> const std::string&;
	auto name() -> std::string&;

	auto size() const -> Index;

	/**
	 * @brief Returns the stored edge indices.
	 * 
	 * @return const ArrayXi& 
	 */
	auto edges() const -> const ArrayXi&;

	/**
	 * @brief Returns the stored domain cell indices.
	 * 
	 * @return const ArrayXi& 
	 */
	auto domainCells() const -> const ArrayXi&;

	/**
	 * @brief Returns the stored ghost cell indices.
	 * 
	 * @return const ArrayXi& 
	 */
	auto ghostCells() const -> const ArrayXi&;

	/**
	 * @brief Return either the domain or ghost cell indices of the boundary
	 * patch.
	 * 
	 * @tparam WhichCells decides whether to return domain or ghost cell indices.
	 * @return const ArrayXi& 
	 */
	template<WhichCells>
	auto cells() const -> const ArrayXi&;

	/**
	 * @brief Runtime variant of BoundaryPatch::cells<WhichCells>
	 * 
	 * @param whichCells decides whether to return domain or ghost cell indices.
	 * @return ArrayXi& 
	 */
	auto cells(WhichCells whichCells) const -> const ArrayXi&;

	bool empty() const;

	template<typename MeshType>
	void updateCellIndices( const MeshBase<MeshType>& );

	template<typename MeshType>
	void setEdgesAndCells( const MeshBase<MeshType>&, ArrayXi&& edges );

	void clearIndices();

	int normalSign() const;

	auto location() const -> BoundaryLocation;

	template<typename MeshType>
	auto outwardNormals( const MeshBase<MeshType>& ) const -> decltype(auto);
	
private:
	template<typename MeshType>
	ArrayXi getDomainCells( const MeshBase<MeshType>&, const ArrayXi& edges ) const;

	template<typename MeshType>
	ArrayXXi getGhostCells( const MeshBase<MeshType>&, const ArrayXi& edges ) const;

	template<typename MeshType>
	void setLocation( const MeshBase<MeshType>& );
};

bool operator< ( const BoundaryPatch&, const BoundaryPatch& );

} // end namespace hms

#include "boundaryPatch.tpp"

#endif
